#include "ORBImageMatch.h"

ORBImageMatch::ORBImageMatch(QWidget* parent)
	: QWidget(parent)
{
	this->setWindowTitle("图像匹配（相似度比较）");
	this->setFixedSize(QSize(320, 480));

	Button* btn1 = new Button(this);
	btn1->setText("选择第一张图");
	Button* btn2 = new Button(this);
	btn2->setText("选择第二张图");
	Button* btn3 = new Button(this);
	btn3->setText("开始匹配");
	Button* btn4 = new Button(this);
	btn4->setText("生成特征文件");
	QVBoxLayout* vLayout = new QVBoxLayout(this);
	vLayout->setAlignment(Qt::AlignTop);
	vLayout->addWidget(btn1);
	vLayout->addWidget(btn2);
	vLayout->addWidget(btn3);
	vLayout->addWidget(btn4);


	connect(btn1, &Button::clicked, [=]() {
		filePath1 = QFileDialog::getOpenFileName(this, tr("请选择图片"), "C:/Users/DBF-DEV-103/Downloads/", tr("Image Files(*.jpg *.png *.webp *.jpeg)"));
		searchImage();
		});

	connect(btn2, &Button::clicked, [=]() {
		filePath2 = QFileDialog::getOpenFileName(this, tr("请选择图片"), "C:/Users/DBF-DEV-103/Downloads/", tr("Image Files(*.jpg *.png *.webp *.jpeg)"));
		});

	connect(btn3, &Button::clicked, [=]() {
		matchImage();
		});

	//选择所有文件并生成特征
	connect(btn4, &Button::clicked, [=]() {
		// 打开文件对话框，选择目录
		QString directory = QFileDialog::getExistingDirectory(this, tr("请选择需要是生成特征的文件"),
		"C:/Users/DBF-DEV-103/Downloads",
		QFileDialog::ShowDirsOnly
		| QFileDialog::DontResolveSymlinks);

	// 检查是否选择了目录
	if (directory.isEmpty()) {
		// 用户没有选择目录
		qDebug() << "请选择目录";
		return;
	}
	//qDebug() << "directory=" << directory;
	// 获取目录下所有文件的列表
	QStringList fileList = QDir(directory).entryList(QDir::Files);
	batchGenerateImageCharacteristics(directory, fileList);
		});
}

void ORBImageMatch::matchImage() {
	// 加载目标图片和仓库图片
	cv::Mat targetImage = cv::imread(filePath1.toStdString().c_str(), cv::IMREAD_GRAYSCALE);
	cv::Mat repositoryImage = cv::imread(filePath2.toStdString().c_str(), cv::IMREAD_GRAYSCALE);

	// 初始化ORB检测器
	cv::Ptr<cv::ORB> orbDetector = cv::ORB::create();
	std::vector<cv::KeyPoint> keypointsRepository, keypointsTarget;
	cv::Mat descriptorsRepository, descriptorsTarget;

	// 检测特征点并计算描述符
	orbDetector->detectAndCompute(repositoryImage, cv::noArray(), keypointsRepository, descriptorsRepository);
	orbDetector->detectAndCompute(targetImage, cv::noArray(), keypointsTarget, descriptorsTarget);

	// 创建暴力匹配器对象
	cv::BFMatcher matcher;
	std::vector<cv::DMatch> matches;

	// 进行匹配
	matcher.match(descriptorsTarget, descriptorsRepository, matches);//对特征带你进行匹配然后比较距离

	// 根据匹配结果判断是否存在
	double minDist = 100;
	for (auto match : matches) {
		qDebug() << "distance=" << match.distance;
		if (match.distance < minDist)
			minDist = match.distance;
	}

	// 设定阈值
	if (minDist < 100) {
		qDebug() << "目标图片在仓库中存在";
	}
	else {
		qDebug() << "目标图片在仓库中不存在";
	}
	//c/c++ opencv保存特征到本地及加载本地特征并匹配相似度
}

//批量生成特征文件
void ORBImageMatch::batchGenerateImageCharacteristics(QString directory, QStringList fileList) {
	for (QString filePath : fileList) {
		qDebug() << "filePath=" << directory + "/" + filePath;
		QString imageFilePath = directory + "/" + filePath;
		cv::Mat targetImage = cv::imread(imageFilePath.toStdString().c_str(), cv::IMREAD_GRAYSCALE);
		cv::Ptr<cv::ORB> orbDetector = cv::ORB::create();
		std::vector<cv::KeyPoint> keypointsTarget;
		cv::Mat  descriptorsTarget;
		// 检测特征点并计算描述符
		orbDetector->detectAndCompute(targetImage, cv::noArray(), keypointsTarget, descriptorsTarget);
		//将得到的特征存起来
		// 创建FileStorage对象并打开文件
		QFileInfo fileInfo(filePath);
		QString fileName = fileInfo.completeBaseName(); // 返回不带扩展名的文件名
		QString mDirectory = directory + "/features/";
		QString fileFullName = "C:/Users/DBF-DEV-103/Downloads/features/" + fileName + ".yml";
		qDebug() << "fileFullName=" << fileFullName;
		cv::FileStorage fs(fileFullName.toStdString().c_str(), cv::FileStorage::WRITE);
		if (!fs.isOpened()) {
			std::cerr << "Error: 无法打开文件" << std::endl;
			return;
		}
		// 将特征存储到文件

		//fs.write(fileName.toStdString(), descriptorsTarget);
		cv::String mFileName = cv::String(fileName.toStdString());
		fs << "feature_" + mFileName << descriptorsTarget;
		// 关闭文件
		fs.release();
		std::cout << descriptorsTarget << std::endl;
	}

}

//搜索图片
void ORBImageMatch::searchImage() {
	// 加载目标图片和仓库图片
	cv::Mat targetImage = cv::imread(filePath1.toStdString().c_str(), cv::IMREAD_GRAYSCALE);

	// 初始化ORB检测器
	cv::Ptr<cv::ORB> orbDetector = cv::ORB::create();
	std::vector<cv::KeyPoint>  keypointsTarget;
	cv::Mat  descriptorsTarget;

	// 检测特征点并计算描述符
	orbDetector->detectAndCompute(targetImage, cv::noArray(), keypointsTarget, descriptorsTarget);

	// 创建暴力匹配器对象
	cv::BFMatcher matcher;
	std::vector<cv::DMatch> matches;

	//加载特征列表
	// 打开文件对话框，选择目录
	QString directory = "C:/Users/DBF-DEV-103/Downloads/features/";

	// 检查是否选择了目录
	if (directory.isEmpty()) {
		// 用户没有选择目录
		qDebug() << "请选择目录";
		return;
	}
	//qDebug() << "directory=" << directory;
	// 获取目录下所有文件的列表
	QStringList fileList = QDir(directory).entryList(QDir::Files);
	vector<QString> vectors;
	for (QString filePath : fileList) {
		QFileInfo fileInfo(filePath);
		QString fileName = fileInfo.completeBaseName(); // 返回不带扩展名的文件名
		QString fileFullName = directory + filePath;
		qDebug() << "fileFullName=" << fileFullName;
		// 初始化FileStorage对象
		cv::FileStorage fs(fileFullName.toStdString().c_str(), cv::FileStorage::READ);
		if (!fs.isOpened()) {
			qDebug() << "Failed to open " << fileFullName;
			return;
		}

		// 假设特征存储在名为"features"的节点下
		cv::Mat descriptors;
		const String feature_name = "feature_" + fileName.toStdString();
		fs[feature_name] >> descriptors;
		// 进行匹配
		matcher.match(descriptorsTarget, descriptors, matches);//对特征带你进行匹配然后比较距离
		// 根据匹配结果判断是否存在
		double minDist = 100;
		for (auto match : matches) {
			qDebug() << "distance=" << match.distance;
			if (match.distance < minDist)
				minDist = match.distance;
		}

		// 设定阈值
		if (minDist < 100) {
			//qDebug() << "目标图片在仓库中存在";
			vectors.push_back(fileFullName);
		}
		else {
			//qDebug() << "目标图片在仓库中不存在";
		}
		// 关闭FileStorage对象
		fs.release();
	}
	if (vectors.size() > 0) {
		qDebug() << "检测到相似图片";
	}
	else {
		qDebug() << "未检测到相似图片";
	}

	for (auto mPath : vectors) {
		qDebug() << "mPath=" << mPath;
	}

}
ORBImageMatch::~ORBImageMatch()
{
}
